package com.zy.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * @author xyz
 */
@Configuration
public class SentinelConfig {

    /**
     * 视图解析器列表：用于展示限流之后的响应内容
     */
    private List<ViewResolver> viewResolvers;

    /**
     * 读写器：用于操作请求和响应
     */
    private ServerCodecConfigurer serverCodecConfigurer;

    /**
     * 有参构造器
     */
    public SentinelConfig(ObjectProvider<List<ViewResolver>> provider,
                          ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = provider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;

    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @PostConstruct
    public void initExceptionResponse() {
        GatewayCallbackManager.setBlockHandler((exchange, throwable) -> {
            Map<String, Object> map = new HashMap<>(2);
            map.put("code", 0);
            map.put("message", "QPS过高，API网关执行限流操作");
            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .body(BodyInserters.fromObject(map));
        });
    }

    @PostConstruct
    public void initApiDefinition() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition groupA = new ApiDefinition("groupA");
        ApiDefinition groupB = new ApiDefinition("groupB");
        ApiPathPredicateItem predicateItemA = new ApiPathPredicateItem();
        predicateItemA.setPattern("/user-app/api/v1/**");
        predicateItemA.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX);
        ApiPathPredicateItem predicateItemB = new ApiPathPredicateItem();
        predicateItemB.setPattern("/user-app/api/v0/**");
        predicateItemB.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX);
        groupA.setPredicateItems(new HashSet() {{
            add(predicateItemA);
        }});
        groupB.setPredicateItems(new HashSet() {{
            add(predicateItemB);
        }});
        definitions.add(groupA);
        definitions.add(groupB);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        GatewayFlowRule ruleA = new GatewayFlowRule("groupA");
        ruleA.setCount(1).setIntervalSec(3);
        GatewayFlowRule ruleB = new GatewayFlowRule("groupB");
        ruleB.setCount(5).setIntervalSec(3);
        rules.add(ruleA);
        rules.add(ruleB);
        GatewayRuleManager.loadRules(rules);
    }

}
